package com.keba.otp.client;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

import javax.microedition.io.Connector;
import javax.microedition.io.HttpConnection;
import javax.microedition.lcdui.Command;
import javax.microedition.lcdui.CommandListener;
import javax.microedition.lcdui.Display;
import javax.microedition.lcdui.Displayable;
import javax.microedition.lcdui.Form;
import javax.microedition.lcdui.Item;
import javax.microedition.lcdui.TextBox;
import javax.microedition.lcdui.TextField;
import javax.microedition.midlet.MIDlet;
import javax.microedition.midlet.MIDletStateChangeException;
import javax.microedition.rms.RecordStore;
import javax.microedition.rms.RecordStoreException;

import com.keba.otp.crypto.Crypto;

/**
 * Main MIDlet.
 * @author bejmuller
 *
 */
public class Main extends MIDlet implements CommandListener {
	/*
	 * login: bejelentkezesi kepernyo
	 * register: regisztracios kepernyo
	 * generate: OTP generalasi es megjelenitesi kepernyo
	 * errScreen: hibakat kozlo kepernyo
	 */
	private Displayable login;
	private Displayable register;
	private Displayable generate;
	private TextBox errScreen;
	private Displayable lastScreen;
	/*
	 * Command: a softkey-ekhez rendelheto elemek, amik megnyomasara kulonbozo dolgok hajtodnak vegre
	 * exit: kilepes
	 * log: bejelentkezes
	 * reg: regisztracio
	 * gen: OTP generalasa
	 * back: Error screenrol valo visszateres
	 */
	private Command	exit;
	private Command	log;
	private Command reg;
	private Command gen;
	private Command back;
	/*
	 * a szerver url-je
	 */
	private String url = "http://bejmuller.homelinux.org:8080/OTP-Server/MobileControlMain";
	/*
	 * s bejelentkezes utan ez hozzarendelodik szerver oldalon a felhasznalohoz, majd
	 * elkuldesre kerul a a mobil fele, ezutan generalaskor ezzel azonositja magat, 
	 * hogy o tenyleg be van jelentkezve
	 */
	private String sessionKey;
	/*
	 * a bejelentkezeshez, regisztraciohoz, es generalashoz szukseges szovegmezok
	 */
	private TextField logUser;
	private TextField logPass;
	private TextField regUser;
	private TextField regPass;
	private TextField tempkey;
	private TextField otpField;

	/**
	 * Konstruktor. Elokesziti a kepernyoket, es hozzarendeli a Commandokat.
	 */
	public Main() {
		this.back = new Command("Back", Command.BACK, 0);
		this.exit = new Command("Exit", Command.EXIT, 1);
		this.log = new Command("Login", Command.OK, 1);
		this.reg = new Command("Register", Command.OK, 1);
		this.gen = new Command("Generate", Command.OK, 1);
		
		this.errScreen = getErrScreen();
		this.errScreen.setCommandListener(this);
		this.errScreen.addCommand(this.back);
		
		this.login = getSreen1();
		this.login.setCommandListener(this);
		this.login.addCommand(this.exit);
		this.login.addCommand(this.log);
		
		this.lastScreen = this.login;
				
		this.register = getSreen2();
		this.register.setCommandListener(this);
		this.register.addCommand(this.exit);
		this.register.addCommand(this.reg);
		
		
		this.generate = getSreen3();
		this.generate.setCommandListener(this);
		this.generate.addCommand(this.exit);
		this.generate.addCommand(this.gen);
		this.generate.addCommand(this.log);
		this.sessionKey = null;
	}
	
	/**
	 * Hiba ("Error") kepernyot hoz letre.
	 * @return a letrehozott hiba kepernyo
	 */
	private TextBox getErrScreen() {
		return new TextBox("Error","",50,TextField.UNEDITABLE);
	}
	
	/**
	 * A bejelentkezo kepernyot hozza letre.
	 * @return a letrehozott login formot tartalmazo kepernyo
	 */
	private Displayable getSreen1() {
		logUser = new TextField("Username", "", 15, TextField.ANY);
		logPass = new TextField("Password", "", 15, TextField.PASSWORD);
		Item[] items = new Item[] {logUser, logPass};
		return new Form("Login",items);
	}
	
	/**
	 * A regisztralo kepernyot hozza letre.
	 * @return a letrehozott regisztralo formot tartalmazo kepernyo
	 */
	private Displayable getSreen2() {
		regUser = new TextField("Username", "", 15, TextField.ANY);
		regPass = new TextField("Password", "", 15, TextField.PASSWORD);
		tempkey = new TextField("Temporary key", "", 15, TextField.PASSWORD);		
		Item[] items = new Item[] {regUser, regPass, tempkey};
		return new Form("Register",items);
	}
	
	/**
	 * Az OTP-t megjelenito kepernyot hozza letre.
	 * @return a letrehozott kepernyo.
	 */
	private Displayable getSreen3() {
		otpField = new TextField("OTP", "", 10, TextField.UNEDITABLE);
		Item[] items = new Item[] {otpField};
		return new Form("Generate", items);
	}
	
	/**
	 * Az alkalmazas elinditasakor fut le. Megallapitja, hogy a "KeyStore" RecordStore letezik-e, 
	 * ha nem akkor a regisztralo kepernyore, ha igen, akkor a login kepernyore visz. 
	 */
	protected void startApp() throws MIDletStateChangeException {
		String[] recordStores = RecordStore.listRecordStores();
		boolean foundRecordStore = false;
		if (recordStores != null)
			for (int i = 0; i < recordStores.length; i++) {
				if (recordStores[i].equals("KeyStore"))
					foundRecordStore = true;
			}
		if (foundRecordStore)
			Display.getDisplay(this).setCurrent(login);
		else
			Display.getDisplay(this).setCurrent(register);
	}

	/**
	 * A kepernyokhoz rendelt Commandok lekezeleset vegzi.
	 */
	public void commandAction(Command command, Displayable displayable) {
		if (command == this.back) {
			Display.getDisplay(this).setCurrent(lastScreen);
		} else
		if (command == this.log) {
			/*
			 * a bejelentkezest vegzi el
			 */
			HttpConnection conn = null;
			OutputStream os = null;
			InputStream is = null;
			String agent = "Mozilla/4.0";
			/*
			 * HTML POST method-hoz szukseges
			 */
			String type = "application/x-www-form-urlencoded";
			try {
				conn = (HttpConnection) Connector.open( url );
				conn.setRequestMethod( HttpConnection.POST );
				conn.setRequestProperty( "User-Agent", agent );
				conn.setRequestProperty( "Content-Type", type );
				
				/*
				 * megnyitja a "KeyStore" RecordStoret, es kiolvassa belole a 
				 * kriptalashoz szukseges kulcsot
				 */
				RecordStore rs = RecordStore.openRecordStore("KeyStore", false);
				byte[] key = rs.getRecord(rs.getNumRecords());
				rs.closeRecordStore();
				String cryptedUser = Crypto.performEncrypt(logUser.getString(), key);
				String cryptedPass = Crypto.performEncrypt(logPass.getString(), key);
				String data = "action=login&user=" + cryptedUser + "&passw=" + cryptedPass;
				conn.setRequestProperty( "Content-Length", String.valueOf(data.length()));

				/*
				 * kiirja a bejelentkezesi adatokat az Output Streamre
				 */
				os = conn.openOutputStream();
				os.write( data.getBytes() );
				os.flush();
				/*
				 * fogadja a sessionKey-t az Input Streamrol
				 */
				is = conn.openInputStream();
				byte[] buf = new byte[64];
				int inL = is.read(buf);
				
				if (inL > 1 ) {
					byte[] mess = new byte[inL];
					for (int i = 0; i < inL; i++) {
						mess[i] = buf[i];
					}
					sessionKey = Crypto.performDecrypt((new String(mess)), key);
					Display.getDisplay(this).setCurrent(generate);
				} else {
					this.lastScreen = this.login;
					errScreen.setString("Bad username or passwod.");
					Display.getDisplay(this).setCurrent(errScreen);
				}
			} catch (IOException e) {
				e.printStackTrace();
				this.lastScreen = this.login;
				errScreen.setString("Connection error. Try again.");
				Display.getDisplay(this).setCurrent(errScreen);
			} catch (RecordStoreException e) {
				e.printStackTrace();
				this.lastScreen = this.login;
				errScreen.setString("RecordStore error.");
				Display.getDisplay(this).setCurrent(errScreen);
			} finally {
				try {
					if (is != null)
						is.close();
			        if (os != null)
			        	os.close();        
			        if (conn != null)
			          conn.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		} else
		if (command == this.gen && sessionKey!=null) {
			/* 
			 * generalast vegzi el
			 * ha a sessionkey nem letezik, akkor nem vagyunk bejelentkezve, tehat nem kerhetunk OTP-t
			 */
			HttpConnection conn = null;
			OutputStream os = null;
			InputStream is = null;
			String agent = "Mozilla/4.0";
			String type = "application/x-www-form-urlencoded";
			try {
				conn = (HttpConnection) Connector.open( url );
				conn.setRequestMethod( HttpConnection.POST );
				conn.setRequestProperty( "User-Agent", agent );
				conn.setRequestProperty( "Content-Type", type );
				conn.setRequestMethod(HttpConnection.POST);
				String data = "action=generate&sessionkey=" + sessionKey;
				conn.setRequestProperty( "Content-Length", String.valueOf(data.length()));

				/*
				 * kiirja az OTP generalasi kerest, es a  sessionKey-t az Output Streamre
				 */
				os = conn.openOutputStream();
				os.write( data.getBytes() );
				os.flush();
				/*
				 * fogadja a generalt OTP-t az Input Streamrol
				 */
				is = conn.openInputStream();
				byte[] buf = new byte[64];
				int inL = is.read(buf);
				
				if (inL > 1) {
					byte[] otp = new byte[inL];
					for (int i = 0; i < inL; i++) {
						otp[i] = buf[i];
					}
					RecordStore rs = RecordStore.openRecordStore("KeyStore", false);
					byte[] key = rs.getRecord(rs.getNumRecords());
					rs.closeRecordStore();
					/*
					 * megjeleniti a kapott OTP-t
					 */
					otpField.setString(Crypto.performDecrypt((new String(otp)), key));
				} else {
					this.lastScreen = this.generate;
					errScreen.setString("You are not logged in");
					Display.getDisplay(this).setCurrent(errScreen);
				}
			} catch (IOException e) {
				this.lastScreen = this.generate;
				errScreen.setString("Connection error. Try again.");
				Display.getDisplay(this).setCurrent(errScreen);
			} catch (RecordStoreException e) {
				this.lastScreen = this.generate;
				errScreen.setString("RecordStore error.");
				Display.getDisplay(this).setCurrent(errScreen);
			} finally {
				try {
					if (is != null)
						is.close();
			        if (os != null)
			        	os.close();        
			        if (conn != null)
			          conn.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		} else
		if (command == this.reg && !regPass.getString().equals("") && !tempkey.getString().equals("") ) {
			/*
			 * regisztraciot vegzi el, ha a ket password mezoben levo szoveg megegyezik
			 */
			HttpConnection conn = null;
			OutputStream os = null;
			InputStream is = null;
			String agent = "Mozilla/4.0";
			String encodedData = "action=register&user=" + regUser.getString() + "&passw=" + regPass.getString() + "&tempkey=" + tempkey.getString();
			String type = "application/x-www-form-urlencoded";
			try {
				conn = (HttpConnection) Connector.open( url );
				conn.setRequestMethod( HttpConnection.POST );
				conn.setRequestProperty( "User-Agent", agent );
				conn.setRequestProperty( "Content-Type", type );
				conn.setRequestProperty( "Content-Length", String.valueOf(encodedData.length()));

				/*
				 * kiirja a regisztracios adatokat az Output Streamre
				 */
				os = conn.openOutputStream();
				os.write( encodedData.getBytes() );
				os.flush();
				
				/*
				 * fogadja a kriptalashoz szukseges key-t az Input Streamrol
				 */
				is = conn.openInputStream();
				byte[] buf = new byte[1024];
				int inL = is.read(buf);
				
				
				/*
				 * letrehozza a KeyStore RecordStore-t, majd eltarolja benne a key-t
				 */
				if (inL > 1) {
					byte[] key = new byte[inL];
					for (int i = 0; i < inL; i++) {
						key[i] = buf[i];
					}
					RecordStore rs = RecordStore.openRecordStore("KeyStore", true, RecordStore.AUTHMODE_PRIVATE, true);
					rs.addRecord(key, 0, inL);
					rs.closeRecordStore();
					Display.getDisplay(this).setCurrent(login);
				} else {
					this.lastScreen = this.register;
					errScreen.setString("Bad username, password, or temporary key.");
					Display.getDisplay(this).setCurrent(errScreen);
				}
			} catch (IOException e) {
				e.printStackTrace();
				this.lastScreen = this.register;
				errScreen.setString("Connection error. Try again.");
				Display.getDisplay(this).setCurrent(errScreen);
			} catch (RecordStoreException e) {
				e.printStackTrace();
				this.lastScreen = this.register;
				errScreen.setString("RecordStore error.");
				Display.getDisplay(this).setCurrent(errScreen);
			}
			finally {
				try {
					if (is != null)
						is.close();
			        if (os != null)
			        	os.close();        
			        if (conn != null)
			          conn.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		} else
		if (command == this.exit) {
			try {
				destroyApp(false);
				notifyDestroyed();
			} catch (MIDletStateChangeException e) {
				e.printStackTrace();
			}
		}
	}
	
	/* (non-Javadoc)
	 * @see javax.microedition.midlet.MIDlet#destroyApp(boolean)
	 */
	protected void destroyApp(boolean unconditional) throws MIDletStateChangeException {}

	/* (non-Javadoc)
	 * @see javax.microedition.midlet.MIDlet#pauseApp()
	 */
	protected void pauseApp() {}
}
